using System.Collections.Concurrent;
using System.Net.Sockets;
using System.Runtime.InteropServices;

namespace muduoDotNet
{
    /// <summary>
    /// Linux平台基于epoll的异步轮询器
    /// 通过P/Invoke调用Linux epoll系统调用
    /// </summary>
    public class EpollPoller : AsyncPoller
    {
        private const int EPOLL_CTL_ADD = 1;
        private const int EPOLL_CTL_MOD = 2;
        private const int EPOLL_CTL_DEL = 3;
        
        private const uint EPOLLIN = 0x001;
        private const uint EPOLLOUT = 0x004;
        private const uint EPOLLERR = 0x008;
        private const uint EPOLLHUP = 0x010;
        private const uint EPOLLET = 0x80000000; // Edge Triggered

        private readonly int _epollFd;
        private readonly Dictionary<int, Channel> _fdToChannel;
        private readonly ManualResetEventSlim _eventSignal;

        public EpollPoller(EventLoop loop) : base(loop)
        {
            if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
            {
                throw new PlatformNotSupportedException("EpollPoller is only supported on Linux");
            }

            _epollFd = epoll_create1(0);
            if (_epollFd < 0)
            {
                throw new SystemException("Failed to create epoll file descriptor");
            }

            _fdToChannel = new Dictionary<int, Channel>();
            _eventSignal = new ManualResetEventSlim(false);
            
            Logger.Info($"EpollPoller created with epoll fd: {_epollFd}");
        }

        public override void StartAsyncOperation(Channel channel, AsyncEventType eventType)
        {
            // epoll是通过UpdateChannel来管理事件的，这里不需要额外操作
            Logger.Debug($"StartAsyncOperation called for channel {channel.Fd}, eventType: {eventType}");
        }

        public override void CancelAsyncOperation(Channel channel)
        {
            RemoveChannel(channel);
        }

        public override void UpdateChannel(Channel channel)
        {
            _loop.AssertInLoopThread();

            var fd = channel.Fd;
            var events = ConvertToEpollEvents(channel.Events);

            lock (_channelsLock)
            {
                if (channel.Events == EventType.None)
                {
                    // 移除Channel
                    if (_fdToChannel.ContainsKey(fd))
                    {
                        EpollCtl(EPOLL_CTL_DEL, fd, 0);
                        _fdToChannel.Remove(fd);
                        _channels.Remove(fd);
                    }
                    channel.SetAddedToPoller(false);
                }
                else
                {
                    // 添加或修改Channel
                    var operation = _fdToChannel.ContainsKey(fd) ? EPOLL_CTL_MOD : EPOLL_CTL_ADD;
                    
                    if (EpollCtl(operation, fd, events) == 0)
                    {
                        _fdToChannel[fd] = channel;
                        _channels[fd] = channel;
                        channel.SetAddedToPoller(true);
                        Logger.Debug($"Channel {fd} {(operation == EPOLL_CTL_ADD ? "added" : "modified")} in epoll with events: 0x{events:X}");
                    }
                    else
                    {
                        Logger.Error($"Failed to {(operation == EPOLL_CTL_ADD ? "add" : "modify")} channel {fd} in epoll");
                        throw new SystemException($"epoll_ctl failed for fd {fd}");
                    }
                }
            }
        }

        public override void RemoveChannel(Channel channel)
        {
            _loop.AssertInLoopThread();
            
            var fd = channel.Fd;
            lock (_channelsLock)
            {
                if (_fdToChannel.ContainsKey(fd))
                {
                    EpollCtl(EPOLL_CTL_DEL, fd, 0);
                    _fdToChannel.Remove(fd);
                    _channels.Remove(fd);
                    Logger.Debug($"Channel {fd} removed from epoll");
                }
            }
            
            channel.SetAddedToPoller(false);
        }

        protected override void WaitForAsyncOperations(int timeoutMs)
        {
            const int maxEvents = 64;
            var events = new EpollEvent[maxEvents];

            try
            {
                int numEvents = epoll_wait(_epollFd, events, maxEvents, timeoutMs);
                
                if (numEvents > 0)
                {
                    Logger.Debug($"epoll_wait returned {numEvents} events");
                    ProcessEpollEvents(events, numEvents);
                    _eventSignal.Set();
                }
                else if (numEvents < 0)
                {
                    Logger.Error($"epoll_wait failed with error code: {numEvents}");
                }
            }
            catch (Exception ex)
            {
                Logger.Error("Error in epoll_wait", ex);
            }
        }

        private void ProcessEpollEvents(EpollEvent[] events, int numEvents)
        {
            for (int i = 0; i < numEvents; i++)
            {
                var epollEvent = events[i];
                var fd = epollEvent.data.fd;

                lock (_channelsLock)
                {
                    if (_fdToChannel.TryGetValue(fd, out var channel))
                    {
                        var asyncEventType = ConvertFromEpollEvents(epollEvent.events);
                        
                        var eventArgs = new AsyncEventArgs
                        {
                            Channel = channel,
                            EventType = asyncEventType,
                            BytesTransferred = 0,
                            SocketError = SocketError.Success
                        };

                        // 检查错误事件
                        if (epollEvent.events.HasFlag(EPOLLERR) || epollEvent.events.HasFlag(EPOLLHUP))
                        {
                            eventArgs.EventType |= AsyncEventType.Error;
                            Logger.Warning($"Error event on fd {fd}: events=0x{epollEvent.events:X}");
                        }

                        AddCompletedEvent(eventArgs);
                        Logger.Debug($"Processed epoll event for fd {fd}: {asyncEventType}");
                    }
                    else
                    {
                        Logger.Warning($"Received epoll event for unknown fd: {fd}");
                    }
                }
            }
        }

        private uint ConvertToEpollEvents(EventType eventType)
        {
            uint events = 0;
            
            if (eventType.HasFlag(EventType.ReadEvent))
                events |= EPOLLIN;
            if (eventType.HasFlag(EventType.WriteEvent))
                events |= EPOLLOUT;
            
            // 使用Edge Triggered模式提高性能
            events |= EPOLLET;
            
            return events;
        }

        private AsyncEventType ConvertFromEpollEvents(uint events)
        {
            AsyncEventType result = AsyncEventType.None;
            
            if (events.HasFlag(EPOLLIN))
                result |= AsyncEventType.Read;
            if (events.HasFlag(EPOLLOUT))
                result |= AsyncEventType.Write;
            if (events.HasFlag(EPOLLERR))
                result |= AsyncEventType.Error;
            if (events.HasFlag(EPOLLHUP))
                result |= AsyncEventType.Close;
            
            return result;
        }

        private int EpollCtl(int operation, int fd, uint events)
        {
            var epollEvent = new EpollEvent
            {
                events = events,
                data = new EpollData { fd = fd }
            };
            
            return epoll_ctl(_epollFd, operation, fd, ref epollEvent);
        }

        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);
            if (disposing)
            {
                if (_epollFd >= 0)
                {
                    close(_epollFd);
                    Logger.Info("Epoll file descriptor closed");
                }
                _eventSignal.Dispose();
            }
        }

        #region Linux系统调用P/Invoke声明

        [DllImport("libc", SetLastError = true)]
        private static extern int epoll_create1(int flags);

        [DllImport("libc", SetLastError = true)]
        private static extern int epoll_ctl(int epfd, int op, int fd, ref EpollEvent @event);

        [DllImport("libc", SetLastError = true)]
        private static extern int epoll_wait(int epfd, [Out] EpollEvent[] events, int maxevents, int timeout);

        [DllImport("libc", SetLastError = true)]
        private static extern int close(int fd);

        [StructLayout(LayoutKind.Sequential)]
        private struct EpollEvent
        {
            public uint events;
            public EpollData data;
        }

        [StructLayout(LayoutKind.Explicit)]
        private struct EpollData
        {
            [FieldOffset(0)]
            public IntPtr ptr;
            [FieldOffset(0)]
            public int fd;
            [FieldOffset(0)]
            public uint u32;
            [FieldOffset(0)]
            public ulong u64;
        }

        #endregion
    }

    /// <summary>
    /// uint扩展方法，用于检查标志位
    /// </summary>
    internal static class UintExtensions
    {
        public static bool HasFlag(this uint value, uint flag)
        {
            return (value & flag) == flag;
        }
    }
}